package app

import (
	"fmt"
	"io"
	"os"
	"time"

	"gitlab.xunlei.cn/xacc/fastlogger"
	"gitlab.xunlei.cn/xacc/xlan"
	nd "gitlab.xunlei.cn/xacc/xlan/net_device"
	"gitlab.xunlei.cn/xacc/xlan/utils"
)

type XlanVpnIo struct {
	xlanDev *nd.ClientNetDevice
	link    *nd.LinkInfo
	lanid   uint64
	pkgId   uint32

	Writer io.Writer
}

func ConnectWithLink(link *nd.LinkInfo) (*XlanVpnIo, error) {
	// 自动生成token和lanid
	lanid := xlan.NewLanID(link.UserId, link.DstNodeId)
	duration := 7 * 24 * time.Hour
	key := os.Getenv("XLAN_SECRET_KEY")
	iv := os.Getenv("XLAN_SECRET_IV")
	token, err := utils.GenerateSDNToken2(key, iv, lanid, link.InnerLocalIp, link.UserId, "", duration, 28)
	if err != nil {
		return nil, err
	}

	// 客户端地址
	xlanAddressCli := []xlan.Address{
		{Protocol: link.IngressProtocol, Address: link.IngressAddress},
	}
	fastlogger.Infof("lanid:%d, token:%s", lanid, token)

	// xlan设备
	xlanClient, err := nd.Dials(xlanAddressCli, lanid, token, 1, nd.WithRWChanSize(12800), nd.WithWriteChanIndexFunc(nd.DefaultTcpUdpChanIndexFunc), nd.WithDownStatOn(0, 1, 10000, 100, 0, time.Second, false, false)) //,
	if err != nil {
		return nil, err
	}

	start := time.Now()
	for i := 0; i < 10; i++ {
		if xlanClient.Status().Status == xlan.STATUS_HEALTH {
			break
		}
		time.Sleep(time.Millisecond * 500)
	}
	if xlanClient.Status().Status != xlan.STATUS_HEALTH {
		return nil, fmt.Errorf("xlan client status not health")
	}

	fastlogger.Infof("xlan client status health, cost %v", time.Since(start))
	return &XlanVpnIo{
		xlanDev: xlanClient.(*nd.ClientNetDevice),
		link:    link,
		lanid:   lanid,
	}, nil
}

func (cv *XlanVpnIo) SetWriter(w io.Writer) {
	cv.Writer = w
}

func (cv *XlanVpnIo) Read(p []byte) (n int, err error) {
	xp, err := cv.xlanDev.ReadPkg(0)
	if err != nil {
		return 0, err
	}
	n = copy(p, xp.GetPayload())
	return n, nil
}

func (cv *XlanVpnIo) Write(p []byte) (n int, err error) {
	xp := xlan.NewXPackage(0x01, 0x01, xlan.FlagToEgress, cv.lanid, uint16(cv.link.DstNodeId), cv.pkgId, p)
	cv.pkgId++
	err = cv.xlanDev.WritePkg(xp)
	if err != nil {
		return 0, err
	}
	return len(p), nil
}

func (cv *XlanVpnIo) Close() error {
	if cv.xlanDev == nil {
		return nil
	}
	return cv.xlanDev.Close()
}
